﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using COFE.Core.Utils;

namespace COFE.Core.Filter
{

    public class FilterParser : IEntryOptionFilterParser
    {
        #region Constructor

        public FilterParser(IPropertySerializer serializer)
        {
            Serializer = serializer;
        }

        public FilterParser()
            : this(new FilterStringSerializer())
        {

        }


        #endregion

        #region Methods

        private static IFilter createFilter(string key, string value)
        {
            var lookupKey = key.TrimStart('-').ToLower();

            if (lookupKey == "filename" || lookupKey == "name")
                if (value.IndexOfAny(new char[] { '*', '?' }) == -1)
                    value = "*" + value + "*";                

            if (FilterUtils2<IEntryFilter>.FilterExists(lookupKey))
                return FilterUtils2<IEntryFilter>.StringToFilter(key, value);
            if (FilterUtils2<IOptionFilter>.FilterExists(lookupKey))
                return FilterUtils2<IOptionFilter>.StringToFilter(key, value);

            var prop = COFEShared.PropertyDictionary.GetProperty(lookupKey);
            if (prop != null)
            {
                string alias = AliasExtension.getAlias(prop);
                if (String.Equals(alias, lookupKey, 
                    StringComparison.InvariantCultureIgnoreCase))
                    return new PropertyEntryFilter(prop, value, alias);
            }
            

            return null;
        }

        /// <summary>
        /// Take a filter string, e.g.
        /// root:{temp} filename:aaa OR filename:"aaabc" filetype:txt
        /// and return a filter array e.g.
        /// { RootFilter, OrFilter (with 2 FileNameFilters), FileTypeFilter }
        /// </summary>
        public void ParseFilter(string filterStr,
            out IEntryFilter[] entryFilters,
            out IOptionFilter[] optionFilters)
        {

            List<IEntryFilter> entryFilterList = new List<IEntryFilter>();
            List<IOptionFilter> optionFilterList = new List<IOptionFilter>();

            Action<IFilter> addToFilterList = (f) =>
            {
                if (f is IEntryFilter)
                    entryFilterList.Add(f as IEntryFilter);
                else if (f is IOptionFilter)
                    optionFilterList.Add(f as IOptionFilter);
            };

            IFilter prevFilter = null, currentFilter = null; ;
            var etor = Serializer.StringToProperty(filterStr).GetEnumerator();

            while (etor.MoveNext())
            {                
                string key = etor.Current.Item1;
                string value = etor.Current.Item2;
                if (key == "OR" && value == "")
                {
                    if (prevFilter is IEntryFilter)
                    {
                        if (etor.MoveNext()) //Next param
                        {
                            key = etor.Current.Item1;
                            value = etor.Current.Item2;

                            IFilter nextFilter = createFilter(key, value);

                            if (nextFilter is IEntryFilter)
                            {
                                if (prevFilter is OrEntryFilter)
                                {
                                    (prevFilter as OrEntryFilter).AddEntryFilter(nextFilter as IEntryFilter);
                                    currentFilter = prevFilter;                                    
                                }
                                else
                                {
                                    currentFilter = new OrEntryFilter(prevFilter as IEntryFilter,
                                        nextFilter as IEntryFilter);                                    
                                }
                                prevFilter = null;
                            }
                            else //OptionFilter, thus add previous and ignore this OR
                            {
                                addToFilterList(prevFilter);
                                prevFilter = nextFilter;
                            }
                        }
                    }
                    //Otherwise ignore the OR
                }
                else
                {
                    currentFilter = createFilter(key, value);
                }

                addToFilterList(prevFilter);
                prevFilter = currentFilter;
            }

            addToFilterList(prevFilter);
            entryFilters = entryFilterList
                .OrderByDescending(ef => ef.GetPriority()).ToArray();
            optionFilters = optionFilterList
                .OrderByDescending(of => of.GetPriority()).ToArray();
        }


        private IEnumerable<Tuple<string, string>>
            getFilterTuples(IEnumerable<IFilter> filters)
        {
            foreach (var filter in filters)
                if (filter is OrEntryFilter)
                {
                    var orFilter = filter as OrEntryFilter;
                    foreach (var subFilter in orFilter.EntryFilters)
                    {
                        yield return new Tuple<string, string>(
                        subFilter.Type, subFilter.Value);

                        if (!orFilter.EntryFilters.Last().Equals(subFilter))
                            yield return new Tuple<string, string>(
                                orFilter.Type, "");
                    }                  
                }
                else
                {
                    yield return new Tuple<string, string>(
                        filter.Type,
                        filter.Value);
                }
        }

        public string FilterToFilterString(IEnumerable<IFilter> filters)
        {
            return Serializer.PropertyToString(getFilterTuples(filters));
        }
        
        #endregion

        #region Data


        #endregion

        #region Public Properties 

        public IPropertySerializer Serializer { get; private set; }
        
        #endregion

       

      

     
    }
}
